﻿#define DO_PARALLEL

using System;
using System.Drawing;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace CParallelImageFilter
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private int[,] m_Filter;
        private int m_Weight;

        // Initialize the filter.
        private void Form1_Load(object sender, EventArgs e)
        {
            m_Filter = new int[,] {
                {-1, 0, 0},
                {0, 0, 0},
                {0, 0, 1}};
            
            m_Weight = 1;
        }

        // Open an image file.
        private void mnuFileOpen_Click(object sender, EventArgs e)
        {
            if (ofdOld.ShowDialog() == DialogResult.OK)
            {
                Bitmap bm = new Bitmap(ofdOld.FileName);
                picOld.Image = (Bitmap)bm.Clone();
                picNew.Image = (Bitmap)bm.Clone();
            }
        }
        //Save an image file.
        private void mnuFileSaveAs_Click(object sender, EventArgs e)
        {
            if (picNew.Image != null)
            {
                if (sfdNew.ShowDialog() == DialogResult.OK)
                {
                    MessageBox.Show("Not implemented", "Not implemented", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    return;
                }
            }
        }
        #region "Filter"
        // Process the image sequentially.
        private void btnFilter_Click(object sender, EventArgs e)
        {
            if (picOld.Image == null)
            {
                MessageBox.Show("You must load an image first", "No Image", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            lblTime.Text = "";
            picNew.Image = (Bitmap)picOld.Image.Clone();
            this.Cursor = Cursors.WaitCursor;
            this.Refresh();

            DateTime start_time = DateTime.Now;
            DoFilter();
            DateTime stop_time = DateTime.Now;
            TimeSpan elapsed_time = stop_time - start_time;

            picNew.Refresh();
            lblTime.Text = elapsed_time.TotalSeconds.ToString("0.00") + " sec";
            this.Cursor = Cursors.Default;
        }

        // Apply the filter sequentially.
        private void DoFilter()
        {
            Bitmap bm1 = (Bitmap)picOld.Image;
            Bitmap bm2 = (Bitmap)picNew.Image;

            for (int x = 1; x <= bm1.Width - 2; x++)
            {
                for (int y = 1; y <= bm1.Height - 2; y++)
                {
                    int r = 0;
                    int g = 0;
                    int b = 0;
                    for (int dx = 0; dx <= 2; dx++)
                    {
                        for (int dy = 0; dy <= 2; dy++)
                        {
                            Color clr = bm1.GetPixel(x + dx - 1, y + dy - 1);
                            r += clr.R * m_Filter[dx, dy];
                            g += clr.G * m_Filter[dx, dy];
                            b += clr.B * m_Filter[dx, dy];
                        }
                    }

                    r = (int)(127 + r / m_Weight);
                    g = (int)(127 + g / m_Weight);
                    b = (int)(127 + b / m_Weight);
                    if (r < 0) r = 0;
                    if (g < 0) g = 0;
                    if (b < 0) b = 0;
                    if (r > 255) r = 255;
                    if (g > 255) g = 255;
                    if (b > 255) b = 255;
                    bm2.SetPixel(x, y, Color.FromArgb(r, g, b));
                }
            }
        }
        #endregion // Filter

        #region "LockBits"
        // Process the image sequentially using LockBits.
        private void btnLockBits_Click(object sender, EventArgs e)
        {
            if (picOld.Image == null)
            {
                MessageBox.Show("You must load an image first", "No Image", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            lblLockBitsTime.Text = "";
            picNew.Image = (Bitmap)picOld.Image.Clone();
            this.Cursor = Cursors.WaitCursor;
            this.Refresh();

            DateTime start_time = DateTime.Now;
            DoFilterLockBits();
            DateTime stop_time = DateTime.Now;
            TimeSpan elapsed_time = stop_time - start_time;

            picNew.Refresh();
            lblLockBitsTime.Text = elapsed_time.TotalSeconds.ToString("0.00") + " sec";
            this.Cursor = Cursors.Default;
        }

        // Apply the filter sequentially.
        private void DoFilterLockBits()
        {
            Bitmap bm1 = (Bitmap)picOld.Image;
            Bitmap bm2 = (Bitmap)picNew.Image;

            // Make BitmapBytesRGB24 objects for the bitmaps.
            BitmapBytesRGB24 bm1_bytes = new BitmapBytesRGB24(bm1);
            BitmapBytesRGB24 bm2_bytes = new BitmapBytesRGB24(bm2);

            // Lock the bitmaps' bytes.
            bm1_bytes.LockBitmap();
            bm2_bytes.LockBitmap();

            int wid = bm1.Width;
            int hgt = bm1.Height;
            byte new_r = 0;
            byte new_g = 0;
            byte new_b = 0;
            for (int x = 1; x <= wid - 2; x++)
            {
                for (int y = 1; y <= hgt - 2; y++)
                {
                    int r = 0;
                    int g = 0;
                    int b = 0;
                    for (int dx = 0; dx <= 2; dx++)
                    {
                        for (int dy = 0; dy <= 2; dy++)
                        {
                            bm1_bytes.GetPixel(x + dx - 1, y + dy - 1, ref new_r, ref new_g, ref new_b);
                            r += new_r * m_Filter[dx, dy];
                            g += new_g * m_Filter[dx, dy];
                            b += new_b * m_Filter[dx, dy];
                        }
                    }

                    r = (int)(127 + r / m_Weight);
                    g = (int)(127 + g / m_Weight);
                    b = (int)(127 + b / m_Weight);
                    if (r < 0) r = 0;
                    if (g < 0) g = 0;
                    if (b < 0) b = 0;
                    if (r > 255) r = 255;
                    if (g > 255) g = 255;
                    if (b > 255) b = 255;
                    bm2_bytes.SetPixel(x, y, (byte)r, (byte)g, (byte)b);
                }
            }

            // Unlock the bitmaps.
            bm1_bytes.UnlockBitmap();
            bm2_bytes.UnlockBitmap();
        }
        #endregion // LockBits

        #region "Parallel LockBits"
        // Process the image in parallel.
        private void btnParallelFilter_Click(object sender, EventArgs e)
        {
            if (picOld.Image == null)
            {
                MessageBox.Show("You must load an image first", "No Image", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            lblParallelTime.Text = "";
            picNew.Image = (Bitmap)picOld.Image.Clone();
            this.Cursor = Cursors.WaitCursor;
            this.Refresh();

            DateTime start_time = DateTime.Now;
            DoFilterLockBitsParallel();
            DateTime stop_time = DateTime.Now;
            TimeSpan elapsed_time = stop_time - start_time;

            picNew.Refresh();
            lblParallelTime.Text = elapsed_time.TotalSeconds.ToString("0.00") + " sec";
            this.Cursor = Cursors.Default;
        }

        // Apply the filter in parallel.
        

        private const int NUM_TASKS = 10;
        private int[] m_X0 = new int[NUM_TASKS];
        private int[] m_X1 = new int[NUM_TASKS];
        private int m_Height;
        private BitmapBytesRGB24 m_Bm1Bytes, m_Bm2Bytes;
        private void DoFilterLockBitsParallel()
        {
            // Get the bitmaps.
            Bitmap bm1 = (Bitmap)picOld.Image;
            Bitmap bm2 = (Bitmap)picNew.Image;

            // Make BitmapBytesRGB24 objects for the bitmaps.
            m_Bm1Bytes = new BitmapBytesRGB24(bm1);
            m_Bm2Bytes = new BitmapBytesRGB24(bm2);

            // Lock the bitmaps' bytes.
            m_Bm1Bytes.LockBitmap();
            m_Bm2Bytes.LockBitmap();

            // Calculate the start and end X values for the strips.
            int wid = (int)(bm1.Width / NUM_TASKS);
            int x0 = 1;
            for (int task_num = 0; task_num <= NUM_TASKS - 1; task_num++)
            {
                m_X0[task_num] = x0;
                m_X1[task_num] = x0 + wid - 1;
                x0 += wid;
            }
            m_X1[NUM_TASKS - 1] = bm1.Width - 2;
            m_Height = bm1.Height;

            // Process the image in strips.
#if (DO_PARALLEL)
            Parallel.For(0, NUM_TASKS, DoFilterStrip);
#else
            for (int task_num = 0; task_num <= NUM_TASKS - 1; task_num++)
            {
                DoFilterStrip(task_num);
            }
#endif

            // Unlock the bitmaps' bytes.
            m_Bm1Bytes.UnlockBitmap();
            m_Bm2Bytes.UnlockBitmap();
        }

        // Process pixels for a strip.
        private void DoFilterStrip(int task_num)
        {
            byte new_r = 0;
            byte new_g = 0;
            byte new_b = 0;
            for (int x = m_X0[task_num]; x <= m_X1[task_num]; x++)
            {
                for (int y = 1; y <= m_Height - 2; y++)
                {
                    int r = 0;
                    int g = 0;
                    int b = 0;
                    for (int dx = 0; dx <= 2; dx++)
                    {
                        for (int dy = 0; dy <= 2; dy++)
                        {
                            m_Bm1Bytes.GetPixel(x + dx - 1, y + dy - 1, ref new_r, ref new_g, ref new_b);
                            r += new_r * m_Filter[dx, dy];
                            g += new_g * m_Filter[dx, dy];
                            b += new_b * m_Filter[dx, dy];
                        }
                    }

                    r = (int)(127 + r / m_Weight);
                    g = (int)(127 + g / m_Weight);
                    b = (int)(127 + b / m_Weight);
                    if (r < 0) r = 0;
                    if (g < 0) g = 0;
                    if (b < 0) b = 0;
                    if (r > 255) r = 255;
                    if (g > 255) g = 255;
                    if (b > 255) b = 255;
                    m_Bm2Bytes.SetPixel(x, y, (byte)r, (byte)g, (byte)b);
                }
            }
        }
        #endregion // Parallel LockBits

        private void mnuFileExit_Click(object sender, EventArgs e)
        {
            Environment.Exit(0);
        }


    }
}
